/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.eclipse.imagen.media.codec;

/**
 * An instance of <code>ImageDecodeParam</code> for decoding images in the PNG format. <code>PNGDecodeParam</code>
 * allows several aspects of the decoding process for PNG images to be controlled. By default, decoding produces output
 * images with the following properties:
 *
 * <p>Images with a bit depth of 8 or less use a <code>DataBufferByte</code> to hold the pixel data. 16-bit images use a
 * <code>DataBufferUShort</code>.
 *
 * <p>Palette color images and non-transparent grayscale images with bit depths of 1, 2, or 4 will have a <code>
 * MultiPixelPackedSampleModel</code> and an <code>IndexColorModel</code>. For palette color images, the <code>
 * ColorModel</code> palette contains the red, green, blue, and optionally alpha palette information. For grayscale
 * images, the palette is used to expand the pixel data to cover the range 0-255. The pixels are stored packed 8, 4, or
 * 2 to the byte.
 *
 * <p>All other images are stored using a <code>PixelInterleavedSampleModel</code> with each sample of a pixel occupying
 * its own <code>byte</code> or <code>short</code> within the <code>DataBuffer</code>. A <code>ComponentColorModel
 * </code> is used which simply extracts the red, green, blue, gray, and/or alpha information from separate <code>
 * DataBuffer</code> entries.
 *
 * <p>Five aspects of this process may be altered by means of methods in this class.
 *
 * <p><code>setSuppressAlpha()</code> prevents an alpha channel from appearing in the output.
 *
 * <p><code>setExpandPalette()</code> turns palette-color images into 3-or 4-channel full-color images.
 *
 * <p><code>setOutput8BitGray()</code> causes 1, 2, or 4 bit grayscale images to be output in 8-bit form, using a <code>
 * ComponentSampleModel</code> and <code>ComponentColorModel</code>.
 *
 * <p><code>setDecodingExponent()</code> causes the output image to be gamma-corrected using a supplied output gamma
 * value.
 *
 * <p><code>setExpandGrayAlpha()</code> causes 2-channel gray/alpha (GA) images to be output as full-color (GGGA)
 * images, which may simplify further processing and display.
 *
 * <p><b> This class is not a committed part of the JAI API. It may be removed or changed in future releases of JAI.</b>
 */
public class PNGDecodeParam implements ImageDecodeParam {

    /** Constructs a default instance of <code>PNGDecodeParam</code>. */
    public PNGDecodeParam() {}

    private boolean suppressAlpha = false;

    /** Returns <code>true</code> if alpha (transparency) will be prevented from appearing in the output. */
    public boolean getSuppressAlpha() {
        return suppressAlpha;
    }

    /**
     * If set, no alpha (transparency) channel will appear in the output image.
     *
     * <p>The default is to allow transparency to appear in the output image.
     */
    public void setSuppressAlpha(boolean suppressAlpha) {
        this.suppressAlpha = suppressAlpha;
    }

    private boolean expandPalette = false;

    /** Returns true if palette-color images will be expanded to produce full-color output. */
    public boolean getExpandPalette() {
        return expandPalette;
    }

    /**
     * If set, palette color images (PNG color type 3) will be decoded into full-color (RGB) output images. The output
     * image may have 3 or 4 channels, depending on the presence of transparency information.
     *
     * <p>The default is to output palette images using a single channel. The palette information is used to construct
     * the output image's <code>ColorModel</code>.
     */
    public void setExpandPalette(boolean expandPalette) {
        this.expandPalette = expandPalette;
    }

    private boolean output8BitGray = false;

    /** Returns the current value of the 8-bit gray output parameter. */
    public boolean getOutput8BitGray() {
        return output8BitGray;
    }

    /**
     * If set, grayscale images with a bit depth less than 8 (1, 2, or 4) will be output in 8 bit form. The output
     * values will occupy the full 8-bit range. For example, gray values 0, 1, 2, and 3 of a 2-bit image will be output
     * as 0, 85, 170, and 255.
     *
     * <p>The decoding of non-grayscale images and grayscale images with a bit depth of 8 or 16 are unaffected by this
     * setting.
     *
     * <p>The default is not to perform expansion. Grayscale images with a depth of 1, 2, or 4 bits will be represented
     * using a <code>MultiPixelPackedSampleModel</code> and an <code>IndexColorModel</code>.
     */
    public void setOutput8BitGray(boolean output8BitGray) {
        this.output8BitGray = output8BitGray;
    }

    private boolean performGammaCorrection = false;

    /**
     * Returns <code>true</code> if gamma correction is to be performed on the image data. The default is <code>false
     * </code>.
     *
     * <p>If gamma correction is to be performed, the <code>getUserExponent()</code> and <code>getDisplayExponent()
     * </code> methods are used in addition to the gamma value stored within the file (or the default value of 1/2.2
     * used if no value is found) to produce a single exponent using the formula:
     *
     * <pre>
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * </pre>
     */
    public boolean getPerformGammaCorrection() {
        return performGammaCorrection;
    }

    /** Turns gamma corection of the image data on or off. */
    public void setPerformGammaCorrection(boolean performGammaCorrection) {
        this.performGammaCorrection = performGammaCorrection;
    }

    private float userExponent = 1.0F;

    /** Returns the current value of the user exponent parameter. By default, the user exponent is equal to 1.0F. */
    public float getUserExponent() {
        return userExponent;
    }

    /**
     * Sets the user exponent to a given value. The exponent must be positive. If not, an <code>IllegalArgumentException
     * </code> will be thrown.
     *
     * <p>The output image pixels will be placed through a transformation of the form:
     *
     * <pre>
     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * </pre>
     *
     * where <code>gamma_from_file</code> is the gamma of the file data, as determined by the <code>gAMA</code>, </code>
     * sRGB</code>, and/or <code>iCCP</code> chunks, and <code>display_exponent</code> is the exponent of the intrinsic
     * transfer curve of the display, generally 2.2.
     *
     * <p>Input files which do not specify any gamma are assumed to have a gamma of <code>1/2.2</code>; such images may
     * be displayed on a CRT with an exponent of 2.2 using the default user exponent of 1.0.
     *
     * <p>The user exponent may be used in order to change the effective gamma of a file. If a file has a stored gamma
     * of X, but the decoder believes that the true file gamma is Y, setting a user exponent of Y/X will produce the
     * same result as changing the file gamma.
     *
     * <p>This parameter affects the decoding of all image types.
     *
     * @throws IllegalArgumentException if <code>userExponent</code> is negative.
     */
    public void setUserExponent(float userExponent) {
        if (userExponent <= 0.0F) {
            throw new IllegalArgumentException(JaiI18N.getString("PNGDecodeParam0"));
        }
        this.userExponent = userExponent;
    }

    private float displayExponent = 2.2F;

    /**
     * Returns the current value of the display exponent parameter. By default, the display exponent is equal to 2.2F.
     */
    public float getDisplayExponent() {
        return displayExponent;
    }

    /**
     * Sets the display exponent to a given value. The exponent must be positive. If not, an <code>
     * IllegalArgumentException</code> will be thrown.
     *
     * <p>The output image pixels will be placed through a transformation of the form:
     *
     * <pre>
     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * </pre>
     *
     * where <code>gamma_from_file</code> is the gamma of the file data, as determined by the <code>gAMA</code>, </code>
     * sRGB</code>, and/or <code>iCCP</code> chunks, and <code>user_exponent</code> is an additional user-supplied
     * parameter.
     *
     * <p>Input files which do not specify any gamma are assumed to have a gamma of <code>1/2.2</code>; such images
     * should be decoding using the default display exponent of 2.2.
     *
     * <p>If an image is to be processed further before being displayed, it may be preferable to set the display
     * exponent to 1.0 in order to produce a linear output image.
     *
     * <p>This parameter affects the decoding of all image types.
     *
     * @throws IllegalArgumentException if <code>userExponent</code> is negative.
     */
    public void setDisplayExponent(float displayExponent) {
        if (displayExponent <= 0.0F) {
            throw new IllegalArgumentException(JaiI18N.getString("PNGDecodeParam1"));
        }
        this.displayExponent = displayExponent;
    }

    private boolean expandGrayAlpha = false;

    /** Returns the current setting of the gray/alpha expansion. */
    public boolean getExpandGrayAlpha() {
        return expandGrayAlpha;
    }

    /**
     * If set, images containing one channel of gray and one channel of alpha (GA) will be output in a 4-channel format
     * (GGGA). This produces output that may be simpler to process and display.
     *
     * <p>This setting affects both images of color type 4 (explicit alpha) and images of color type 0 (grayscale) that
     * contain transparency information.
     *
     * <p>By default, no expansion is performed.
     */
    public void setExpandGrayAlpha(boolean expandGrayAlpha) {
        this.expandGrayAlpha = expandGrayAlpha;
    }

    private boolean generateEncodeParam = false;

    private PNGEncodeParam encodeParam = null;

    /**
     * Returns <code>true</code> if an instance of <code>PNGEncodeParam</code> will be available after an image has been
     * decoded via the <code>getEncodeParam</code> method.
     */
    public boolean getGenerateEncodeParam() {
        return generateEncodeParam;
    }

    /**
     * If set, an instance of <code>PNGEncodeParam</code> will be available after an image has been decoded via the
     * <code>getEncodeParam</code> method that encapsulates information about the contents of the PNG file. If not set,
     * this information will not be recorded and <code>getEncodeParam()</code> will return <code>null</code>.
     */
    public void setGenerateEncodeParam(boolean generateEncodeParam) {
        this.generateEncodeParam = generateEncodeParam;
    }

    /**
     * If <code>getGenerateEncodeParam()</code> is <code>true</code>, this method may be called after decoding has
     * completed, and will return an instance of <code>PNGEncodeParam</code> containing information about the contents
     * of the PNG file just decoded.
     */
    public PNGEncodeParam getEncodeParam() {
        return encodeParam;
    }

    /**
     * Sets the current encoder param instance. This method is intended to be called by the PNG decoder and will
     * overwrite the current instance returned by <code>getEncodeParam</code>.
     */
    public void setEncodeParam(PNGEncodeParam encodeParam) {
        this.encodeParam = encodeParam;
    }
}
